﻿using System;
using System.Text;
using Microsoft.Practices.RecipeFramework;
using Microsoft.Practices.Common;
using Microsoft.Practices.ComponentModel;
using System.ComponentModel.Design;
using System.Collections.Specialized;
using Sixeyed.CodeGeneration.Generation;
using Sixeyed.Guidance.CodeGeneration.ValueProviders.Helpers;
using System.IO;
using Microsoft.Practices.RecipeFramework.Services;
using Sixeyed.Guidance.CodeGeneration.Helpers;

namespace Sixeyed.Guidance.CodeGeneration.ValueProviders
{
    /// <summary>
    /// Provides configured <see cref="Sixeyed.CodeGeneration.Metadata.TemplateConfiguration"/> value
    /// </summary>
    [ServiceDependency(typeof(IDictionaryService))]
    [ServiceDependency(typeof(IExecutionService))]
    public class TemplateConfigurationValueProvider : ValueProvider, IAttributesConfigurable
    {
        private TemplateConfiguration _templateConfiguration;

        /// <summary>
        /// Gets/sets the T4 template configuration
        /// </summary>
        public TemplateConfiguration TemplateConfiguration
        {
            get 
            {
                if (this._templateConfiguration == null)
                {
                    this._templateConfiguration = new TemplateConfiguration();
                }
                return _templateConfiguration; 
            }
            set { _templateConfiguration = value; }
        }

        /// <summary>
        /// Configures provider, loading T4 template configuration from arguments
        /// </summary>
        /// <param name="attributes">Argument dictionary</param>
        public void Configure(StringDictionary attributes)
        {
            this.TemplateConfiguration.TemplatePath = ValueProviderHelper.GetMandatoryArgumentValue(attributes, ArgumentName.TemplatePath, "TemplatePath argument is required");
            this.ConfigureTemplatePath();
            this.TemplateConfiguration.ClassName = ValueProviderHelper.GetOptionalArgumentValue(attributes, ArgumentName.ClassName);
            this.TemplateConfiguration.FileExtension = ValueProviderHelper.GetOptionalArgumentValue(attributes, ArgumentName.FileExtension);
            this.TemplateConfiguration.FileName = ValueProviderHelper.GetOptionalArgumentValue(attributes, ArgumentName.FileName);
            this.TemplateConfiguration.TargetNamespace = ValueProviderHelper.GetOptionalArgumentValue(attributes, ArgumentName.TargetNamespace);
        }

        /// <summary>
        /// Provides updated argument value, if not already populated
        /// </summary>
        /// <param name="currentValue">Current argument value</param>
        /// <param name="newValue">New argument value</param>
        /// <returns>Whether argument value was updated</returns>
        public override bool OnBeginRecipe(object currentValue, out object newValue)
        {
            bool updated = false;
            //only update if the value isn't set:
            if (currentValue != null)
            {
                newValue = null;
            }
            else
            {
                newValue = this.TemplateConfiguration;
                updated = true;
            }
            return updated;
        }

        private void ConfigureTemplatePath()
        {
            string templateDirectory = Path.GetDirectoryName(this.TemplateConfiguration.TemplatePath);
            if (string.IsNullOrEmpty(templateDirectory))
            {
                GuidancePackage package = (GuidancePackage)GetService(typeof(IExecutionService));
                templateDirectory = PathHelper.GetTemplateDirectory(package);
                this.TemplateConfiguration.TemplatePath = Path.Combine(templateDirectory, this.TemplateConfiguration.TemplatePath);
            }
            if (!File.Exists(this.TemplateConfiguration.TemplatePath))
            {
                throw new ArgumentException("File: {0} not found", "TemplateConfiguration.TemplatePath");
            }
        }

        private struct ArgumentName
        {
            public const string TemplatePath = "TemplatePath";
            public const string ClassName = "ClassName";
            public const string FileExtension = "FileExtension";
            public const string FileName = "FileName";
            public const string TargetNamespace = "TargetNamespace";
        }
    }
}
